#pragma once

#include <iostream>
#include <poll.h>
#include <sys/time.h>
#include "Socket.hpp"

static const int fd_num_max = 64;
static const uint16_t defaultport = 8888;
int non_event = 0;
int defaultfd = -1;

class PollServer
{
public:
    PollServer(uint16_t port = defaultport)
        :port_(port)
    {
        // 将所有的文件描述符事件（poll）都初始化
        for(int i = 0; i < fd_num_max; i++)
        {
            _event_fds[i].fd = defaultfd;
            _event_fds[i].events = non_event;   // 用户告诉内核的事件
            _event_fds[i].revents = non_event;  // 内核告诉用户的事件
        }
    }
    ~PollServer()
    {}
public:
    void Init()
    {
        // 通信连接套接字、
        listensock_.Socket();
        listensock_.Bind(port_);
        listensock_.Listen();
    }
    void Start()
    {
        _event_fds[0].fd = listensock_.Fd();    // 维护的文件描述符数组第一个元素和套接字相关联起来
        _event_fds[0].events = POLLIN;          // 给该文件描述符设为可读事件
        int timeout = 3000;     // 3s

        while(true)
        {
            int n = poll(_event_fds, fd_num_max, timeout);  // 文件描述符数组，数组长度，检测时间timeout
            switch(n)
            {
            case 0:
                std::cout << "time out..." << std::endl;      // 检测时间超时了，也没有数据读上来
                break;
            case -1:
                std::cerr << "poll err..." << std::endl;
                break;
            default:
                // 读取成功，有事件就绪了
                std::cout << "get a new link..." << std::endl;
                Dispatcher();   // 任务分发器
                break;
            }
        }
    }
private:
    // 看我们维护的文件描述符数组内部的文件情况
    void PrintFd()
    {
        std::cout << "online fd list: ";
        for(int i = 0; i < fd_num_max; i++)
        {
            if(_event_fds[i].fd == defaultfd)
                continue;
            std::cout << _event_fds[i].fd << ", ";
        }
        std::cout << std::endl;
    }

    // 连接管理器
    void Accepter()
    {
        // 该数组内有文件描述符与 listen套接字相关联,服务器可以 accept了
        // 即，我们的连接事件就绪了
        std::string clientip;
        uint16_t clientport = 0;
        int sock = listensock_.Accept(&clientip, &clientport);
        if(sock < 0)    return;
        lg(Info, "accept success, %s:%d, sock fd: %d", clientip.c_str(), clientport, sock);

        // 三次握手成功
        int pos = 1;
        for(; pos < fd_num_max; pos++)
        {
            if(_event_fds[pos].fd != defaultfd)
            {
                continue;
            }
            else break;
        }
        if(pos == fd_num_max)
        {
            lg(Warning, "server is full, close %d now", sock);  // 服务端的任务已经满了，需要关闭多余来的任务
            close(sock);
        }
        else
        {
            // 没有满，有任务空间
            _event_fds[pos].fd = sock;
            _event_fds[pos].events = POLLIN;        // 用户告诉内核有写的事件
            _event_fds[pos].revents = non_event;    // 内核什么也不需要告诉用户

            PrintFd();  // 看我们维护的文件描述符数组内部的文件情况
        }
    }

    // 没有listen连接
    void Recver(int fd, int i)
    {
        char buffer[1024];
        ssize_t n = read(fd, buffer, sizeof(buffer)-1);
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout << "get a message: " << buffer << std::endl;
        }
        else if(n == 0)
        {
            lg(Info, "client quit, me too, close fd is: %d", fd);
            close(fd);
            _event_fds[i].fd = defaultfd;   // 本质是从poll 内移除
        }
        else
        {
            lg(Warning, "recv error, fd is %d", fd);
            close(fd);
            _event_fds[i].fd = defaultfd;
        }
    }

    void Dispatcher()   // 任务分发器
    {
        for(int i = 0; i < fd_num_max; i++) // j对多个文件描述符进行操作
        {
            int fd = _event_fds[i].fd;
            if(fd == defaultfd)
            {
                continue;
            }

            if(_event_fds[i].revents & POLLIN)  // 判断是否有 POLLIN 读事件
            {
                if(fd == listensock_.Fd())
                {
                    Accepter();     // 连接管理器
                }
                else    // non listenfd, 没有连接，用户维护的文件描述符数组内有元素没有listen
                {   
                    Recver(fd, i);
                }
            }
        }
    }

private:
    Sock listensock_;
    uint16_t port_;

    struct pollfd _event_fds[fd_num_max];    // 数组，用户自己维护的
};